/******************************************************************************

  File Name     : mp_mat.h
  Version       : Initial Draft
  Author        : tongzhilin
  Created       : 2020/8/10
  Last Modified :
  Description   : mp_mat
  Function List :
  History       :
  1.Date        : 2020/8/10
    Author      : tongzhilin
    Modification: Created file

******************************************************************************/
#pragma once
#include "mp_common.h"
#include "mp_allocator.h"

template <typename T> 
T aligned(T x, T alignment = 32)
{
    if (x % alignment != 0) {
        x = x - x % alignment + alignment;
    }
    return x;
    // return (x + alignment-1) & ~(alignment-1);
}

template <typename T> using AlignedVector = std::vector<T,mp_allocator<T, 16>>;
class MpSize
{
public:
    MpSize(ULONG _s32Rows, ULONG _s32Cols):m_s32Rows(_s32Rows), m_s32Cols(_s32Cols){}
    MpSize():m_s32Rows(0), m_s32Cols(0){}
    virtual ~MpSize() {}

    ULONG rows() const { return m_s32Rows; }
    ULONG &rows() { return m_s32Rows; }
    ULONG cols() const { return m_s32Cols; }
    ULONG &cols() { return m_s32Cols; }
    ULONG height() const { return rows(); }
    ULONG &height() { return rows(); }
    ULONG width() const { return cols(); }
    ULONG &width() { return cols(); }

    bool operator == (const MpSize &rhs) const
    {
        return rows() == rhs.rows() && cols() == rhs.cols();
    }

private:
    ULONG m_s32Rows;
    ULONG m_s32Cols;
};

class MpShape: public MpSize
{
public:
    MpShape(ULONG _s32Rows, ULONG _s32Cols, ULONG _s32Channels):MpSize(_s32Rows, _s32Cols), m_s32Channels(_s32Channels) { }
    virtual ~MpShape() {}

    ULONG channels() const { return m_s32Channels; }

    bool operator == (const MpShape &rhs) const
    {
        return MpSize::operator==(rhs) && channels() == rhs.channels();
    }

private:
    ULONG m_s32Channels;
};

template <typename T>
class MpMat
{
private:
    ULONG m_s32Rows;
    ULONG m_s32Cols;
    ULONG m_s32Channels;
    ULONG m_s32Step;
    ULONG m_s32Offset;
    std::shared_ptr<T> m_spData;

public:
    void *raw_ptr()
    { return static_cast<void*>(m_spData.get() + m_s32Offset); }
    const void *raw_ptr() const
    { return static_cast<void*>(m_spData.get() + m_s32Offset); }

    MpMat() :   m_s32Rows(0), m_s32Cols(0), m_s32Channels(0), m_s32Step(0),m_s32Offset(0),m_spData(nullptr){ };
    MpMat(ULONG _s32Rows, ULONG _s32Cols, ULONG _s32Channels, ULONG _s32Step);
    MpMat(ULONG _s32Rows, ULONG _s32Cols, ULONG _s32Channels);

    MpMat(ULONG _s32Rows, ULONG _s32Cols, ULONG _s32Channels, T *_pdata);
    MpMat(ULONG _s32Rows, ULONG _s32Cols, ULONG _s32Channels, ULONG _s32Step, T *_pdata);

    MpMat(const MpMat<T> &rhs);
    MpMat(const MpMat<T> &rhs, ULONG _s32RowOffset, ULONG _s32RowCount, ULONG _s32ColOffset, ULONG _s32ColCount);
    MpMat<T> &operator=(const MpMat<T> &rhs);

    T &at(ULONG r, ULONG c, ULONG ch);
    const T &at(ULONG r, ULONG c, ULONG ch) const;

    MpMat<T> clone() const;

    void read(const T *src);
    void write(T *dst) const;

    const T *ptr(ULONG r = 0) const
    {
        return static_cast<const T*>(raw_ptr()) + r * m_s32Step;
    }
    T *ptr(ULONG r = 0)
    {
        return static_cast<T*>(raw_ptr()) + r * m_s32Step;
    }
    ULONG height() const
    {
        return rows();
    }
    ULONG width() const
    {
        return cols();
    }
    ULONG rows() const
    {
        return m_s32Rows;
    }
    ULONG cols() const
    {
        return m_s32Cols;
    }
    ULONG channels() const
    {
        return m_s32Channels;
    }
    ULONG step() const
    {
        return m_s32Step;
    }
    ULONG total_nr_elem() const
    {
        return rows() * cols() * channels();
    }
    ULONG total_span_elem() const
    {
        return rows() * step();
    }
    bool equals(const MpMat<T> &rhs) const;
    bool is_continuous() const;

    MpSize size() const
    {
        return {rows(), cols()};
    }
    MpShape shape() const
    {
        return {rows(), cols(), channels()};
    }
};

using MpMat8u = MpMat<UCHAR>;
using MpMat32f = MpMat<float>;

